package com.gaj.util;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;

/**
 * ZIP压缩包校验工具类
 *
 * 职责：
 * 1. 数据包命名校验
 * 2. 数据包完整性校验（MD5）
 * 3. 包结构校验
 * 4. 数据完整性校验
 * 5. 文件校验
 *
 * 使用场景：
 * 政法智能办案平台ZIP文件的完整性校验
 *
 * @author 系统
 * @version 1.0
 */
public class ZipVerification {

    // ==================== 命名校验方法 ====================

    /**
     * 数据包命名校验
     *
     * 校验规则：
     * 业务包命名规则：协同业务代码_协同流程节点编号_发送单位代码_接收单位代码_UUID_MD5.zip
     *
     * 命名格式：
     * - 协同业务代码：字母数字组合
     * - 协同流程节点编号：字母数字组合
     * - 发送单位代码：字母数字组合
     * - 接收单位代码：字母数字组合
     * - UUID：36位十六进制字符（包含连字符）
     * - MD5：32位十六进制字符
     * - 文件扩展名：.zip
     *
     * @param zipName ZIP文件名
     * @return 校验是否通过
     */
    public static boolean verifyZipName(String zipName) {
        if (zipName == null || zipName.trim().isEmpty()) {
            System.out.println("ZIP文件名为空，校验不通过");
            return false;
        }

        //System.out.println("开始校验ZIP文件名: " + zipName);

        // 业务包命名规则：协同业务代码_协同流程节点编号_发送单位代码_接收单位代码_UUID_MD5.zip
        String regStr = "^[A-Za-z0-9]+_[A-Za-z0-9]+_[A-Za-z0-9]+_[A-Za-z0-9]+_[a-f0-9\\-]{36}_[A-F0-9]{32}\\.zip$";

        boolean isValid = zipName.matches(regStr);
        if (isValid) {
            //System.out.println("ZIP文件名格式校验通过");
        } else {
            //System.out.println("ZIP文件名格式校验不通过");
        }

        // 临时返回true，实际使用时应该使用上面的校验结果
        return true;
    }

    // ==================== 完整性校验方法 ====================

    /**
     * 数据包完整性校验（MD5）
     *
     * 校验流程：
     * 1. 从文件名提取MD5值
     * 2. 计算数据包的MD5值
     * 3. 比较两个MD5值是否一致
     *
     * @param zipName ZIP文件名
     * @param encryptedData 加密后的数据包
     * @return 校验是否通过
     */
    public static boolean verifyZipCode(String zipName, byte[] encryptedData) {
        //System.out.println("开始MD5完整性校验: " + zipName);

        // 1. 从文件名提取MD5
        String expectedMd5 = extractMd5FromFileName(zipName);

        // 2. 计算数据包的MD5值
        String actualMd5 = calculateMd5(encryptedData);

        // 3. MD5校验
        boolean isValid = verifyMd5(encryptedData, expectedMd5);
        if (isValid) {
            //System.out.println("MD5完整性校验通过");
        } else {
            System.err.println("MD5完整性校验不通过");
        }

        return isValid;
    }

    // ==================== 包结构校验方法 ====================

    /**
     * 包结构校验（带XML解析选项）
     *
     * 校验规则：
     * 要求内容如下，可以少，但不能多
     *
     * 标准结构：
     * xx.zip
     *     ├─YWXX.xml
     *     ├─DZJZ/
     *     ├─WSXX/
     *     ├─SACW/
     *     └─QTFJ/
     *
     * @param inputStream ZIP文件输入流
     * @return 校验是否通过
     */
    public static boolean verifyZipStructure(InputStream inputStream) {
        // 定义有效的条目名称
        HashSet<String> validEntries = new HashSet<>();
        validEntries.add("YWXX.xml");
        validEntries.add("DZJZ/");
        validEntries.add("WSXX/");
        validEntries.add("SACW/");
        validEntries.add("QTFJ/");

        try (ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(inputStream, "GBK", true)) {
            ZipArchiveEntry entry;

            while ((entry = zipInputStream.getNextEntry()) != null) {
                String entryName = entry.getName();
                //System.out.println("  发现条目: " + entryName);

                // 检查条目是否在有效列表中
                if ("YWXX.xml".equals(entryName)) {
                    // 使用dom4j读取YWXX.xml文件
                    try {
                        //parseYWXXXmlWithDom4j(zipInputStream);
                    } catch (Exception e) {
                        System.err.println("    YWXX.xml文件解析失败: " + e.getMessage());
                    }
                    continue;
                }
                if (entryName.contains("DZJZ/")) {
                    //System.out.println("    有效条目: DZJZ/");
                    continue;
                }
                if (entryName.contains("WSXX/")) {
                    //System.out.println("    有效条目: WSXX/");
                    continue;
                }
                if (entryName.contains("SACW/")) {
                    //System.out.println("    有效条目: SACW/");
                    continue;
                }
                if (entryName.contains("QTFJ/")) {
                    //System.out.println("    有效条目: QTFJ/");
                    continue;
                }

                // 如果条目不在有效列表中，校验失败
                System.out.println("    无效条目: " + entryName);
                return false;
            }

            return true;

        } catch (IOException e) {
            System.err.println("ZIP包结构校验失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 使用dom4j解析YWXX.xml文件
     *
     * 解析流程：
     * 1. 将ZIP输入流转换为ByteArrayInputStream
     * 2. 使用SAXReader解析XML文档
     * 3. 获取根元素并开始解析
     * 4. 调用ReadXml进行业务解析
     *
     * @param zipInputStream ZIP文件输入流（当前指向YWXX.xml条目）
     * @throws Exception 解析异常
     */
    private static void parseYWXXXmlWithDom4j(ZipArchiveInputStream zipInputStream) throws Exception {
        // 1. 将ZIP输入流中的XML内容读取到字节数组中
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int bytesRead;

        while ((bytesRead = zipInputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }

        byte[] xmlContent = outputStream.toByteArray();
        //System.out.println("    YWXX.xml文件大小: " + xmlContent.length + " 字节");

        // 2. 创建ByteArrayInputStream用于XML解析
        ByteArrayInputStream xmlInputStream = new ByteArrayInputStream(xmlContent);

        // 3. 使用SAXReader解析XML文档
        ReadXml.execute(xmlInputStream);

        // 关闭输入流
        xmlInputStream.close();
    }

    /**
     * 从文件路径提取MD5值
     *
     * 提取逻辑：
     * 根据文档3.2数据包命名规则，从文件名中提取MD5值
     *
     * 文件名格式示例: DkJ_OGhHgzCAJNJXAAJjULp8TuM805.zip
     * 假设最后一段为MD5值（实际需根据文档命名规则调整提取逻辑）
     *
     * @param filePath 文件路径
     * @return MD5值
     */
    public static String extractMd5FromFileName(String filePath) {
        //System.out.println("开始从文件名提取MD5: " + filePath);

        File file = new File(filePath);
        String fileName = file.getName();
        //System.out.println("文件名: " + fileName);

        // 移除文件后缀
        String nameWithoutExt = fileName.substring(0, fileName.lastIndexOf('.'));
        //System.out.println("去除扩展名: " + nameWithoutExt);

        // 根据实际命名规则提取MD5（此处为示例逻辑，需根据文档调整）
        String[] parts = nameWithoutExt.split("_");
        String md5 = parts.length > 0 ? parts[parts.length - 1] : "";

        //System.out.println("提取的MD5: " + md5);
        return md5;
    }

    /**
     * MD5校验
     *
     * 校验逻辑：
     * 1. 创建MD5摘要算法实例
     * 2. 计算数据的MD5值
     * 3. 转换为十六进制字符串
     * 4. 与期望的MD5值比较
     *
     * @param data 要校验的数据
     * @param expectedMd5 期望的MD5值
     * @return 校验是否通过
     */
    public static boolean verifyMd5(byte[] data, String expectedMd5) {
        //System.out.println("开始MD5校验，数据大小: " + data.length + " 字节，期望MD5: " + expectedMd5);

        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            System.err.println("MD5算法不可用: " + e.getMessage());
            return false;
        }

        if (md5 == null) {
            System.err.println("MD5算法实例创建失败");
            return false;
        }

        // 计算数据的MD5值
        byte[] digest = md5.digest(data);

        // 转换为大写十六进制字符串
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            sb.append(String.format("%02X", b));
        }

        String actualMd5 = sb.toString();
        //System.out.println("计算得到的MD5: " + actualMd5);

        // 比较MD5值
        boolean isValid = actualMd5.equals(expectedMd5.toUpperCase());
        if (isValid) {
            //System.out.println("MD5校验通过");
        } else {
            System.out.println("MD5校验不通过");
        }

        return isValid;
    }

    /**
     * 计算数据的MD5值
     *
     * @param data 要计算的数据
     * @return MD5值（十六进制字符串）
     */
    public static String calculateMd5(byte[] data) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] digest = md5.digest(data);

            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02X", b));
            }

            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            System.err.println("MD5算法不可用: " + e.getMessage());
            return "";
        }
    }
}
